package pl.edu.mimuw.irs.core.model.design.resource;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import pl.edu.mimuw.irs.core.util.Tools;

public abstract class AbstractRolesHolder implements IHolder<RoleInstance>, Serializable {

	private static final long serialVersionUID = 3207915010623195977L;

	// *** Properties
	public static final String ROLE = "ROLE";
	public static final String NAME = "NAME";
	
	// *** Attributes

	private PropertyChangeSupport listeners;
	private String name;
	/* 
	 * Set of custom roles that an actor is capable of acting or 
	 * resource requires to be met by one actor.
	 */
	private Map<Role, RoleInstance> rolesMap;
	// Getters and setters
	public PropertyChangeSupport getListeners() {
		if (listeners == null) listeners = new PropertyChangeSupport(this);
		return listeners;
	}
	public String getName() {
		if (this.name == null) return createNewName();
		return name;
	}
	public void setName(String name) {
		String oldName = this.name;
		this.name = name;
		getListeners().firePropertyChange(NAME, oldName, name);
	}
	public Map<Role, RoleInstance> getRolesMap() {
		if (this.rolesMap == null) this.rolesMap = new HashMap<Role, RoleInstance>();
		return Collections.unmodifiableMap(this.rolesMap);
	}
	public void setRoles(Collection<RoleInstance> roles) {
		Map<Role, RoleInstance> oldMap = getRolesMap();
		rolesMap = new HashMap<Role, RoleInstance>();
		if (roles == null) return;
		for (RoleInstance role : roles) {
			rolesMap.put(role.getRole(), role);
			IHolder<RoleInstance> oldParent = role.getHolder();
			role.setParent(this);
			if (oldParent != null) oldParent.remove(role);
		}
		getListeners().firePropertyChange(ROLE, oldMap, rolesMap);
	}
	public boolean add(RoleInstance roleInstance) {
		getRolesMap();
		RoleInstance lastRole = rolesMap.put(roleInstance.getRole(), roleInstance);
		roleInstance.setParent(this);
		getListeners().firePropertyChange(ROLE, lastRole, roleInstance);
		return true;
	}
	
	public boolean remove(RoleInstance roleInstance) {
		getRolesMap();
		rolesMap.remove(roleInstance.getRole());
		roleInstance.setParent(null);
		getListeners().firePropertyChange(ROLE, roleInstance, null);
		return true;
	}
	
	public boolean remove(Role role) {
		RoleInstance instance = getRolesMap().get(role);
		if (instance == null) return false;
		return remove(instance);
	}
	
	// *** Constructors
	
	public AbstractRolesHolder(String name, Collection<RoleInstance> roleInstances) {
		setRoles(roleInstances);
		setName(name);
	}
	
	public AbstractRolesHolder(Collection<RoleInstance> roleInstances) {
		this(null, roleInstances);
	}
	
	// *** Methods
	
	protected abstract String getNamePrefix();
	
	private String createNewName() {
		StringBuffer nameBuffer = new StringBuffer();
		nameBuffer.append(getNamePrefix());
		if (getRolesMap().size() > 0) {
			nameBuffer.append("[");
			for (RoleInstance instance : getRolesMap().values()) {
				nameBuffer.append(Tools.abbreviation(instance.toString()));
				nameBuffer.append(";");
			}
			nameBuffer.replace(nameBuffer.lastIndexOf(";"), 
					nameBuffer.length(), "]");
		}
		return nameBuffer.toString();
	}
	
	@Override
	public String toString() {
		return getName();
	}
	
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		getListeners().addPropertyChangeListener(listener);
	}
	
	public void removePropertyChangeListener(PropertyChangeListener listener) {
		getListeners().removePropertyChangeListener(listener);
	}
}
